package com.opennews.openplatform.security

import com.opennews.openplatform.RequestMap
import groovy.transform.CompileStatic

/**
 * Used by RequestMap.configAttribute in Spring Security Rest.
 *
 * Spring Security expressions
 * https://grails.github.io/grails-spring-security-core/4.0.x/index.html
 * Table 6. Spring Security expressions
 * Table 7. Traditional configurations and associated expressions
 *
 * - hasRole(role): Returns true if the current principal has the specified role
 * - hasAnyRole([role1,role2]): Returns true if the current principal has any of the supplied roles (given as a comma-separated list of strings)
 * - principal: Allows direct access to the principal object representing the current user
 * - authentication: Allows direct access to the current Authentication object obtained from the SecurityContext
 * - permitAll: Always evaluates to true
 * - denyAll: Always evaluates to false
 * - isAnonymous(): Returns true if the current principal is an anonymous user
 * - isRememberMe(): Returns true if the current principal is a remember-me user
 * - isAuthenticated(): Returns true if the user is not anonymous
 * - isFullyAuthenticated(): Returns true if the user is not an anonymous or a remember-me user
 * - request: the HTTP request, allowing expressions such as “isFullyAuthenticated() or request.getMethod().equals('OPTIONS')”
 */
@CompileStatic
class RequestMapManager {
    static final String URL_SINGLE_WILDCARD = "/*"
    static final String URL_DOUBLE_WILDCARD = "/**"
    static final String DENY_ALL = "denyAll"
    static final String PERMIT_ALL = "permitAll"
    static final String IS_AUTHENTICATED_PREFIX = "isAuthenticated"
    static final String IS_AUTHENTICATED = IS_AUTHENTICATED_PREFIX + "()"
    static final String IS_FULLY_AUTHENTICATED_PREFIX = "isFullyAuthenticated"
    static final String IS_FULLY_AUTHENTICATED = IS_FULLY_AUTHENTICATED_PREFIX + "()"
    static final String HAS_ROLE_PREFIX = "hasRole"
    static final String HAS_ANY_ROLE_PREFIX = "hasAnyRole"
    static final String HAS_NO_ROLE_PREFIX = "hasNoRole"
    static final String HAS_NO_ANY_ROLE_PREFIX = "hasNoAnyRole"

    private static List<RequestMap> cachedRequestMaps

    static List<RequestMap> getCachedRequestMaps() {
        if (cachedRequestMaps == null) {
            reloadCachedRequestMaps()
        }

        return cachedRequestMaps
    }

    static void reloadCachedRequestMaps() {
        cachedRequestMaps = RequestMap.list()
    }

    /**
     * Returns the expression like hasRole('ROLE_ADMIN').
     * @param roleAuthority: It is the string of role authority. AKA role name.
     */
    static String hasRole(String roleAuthority) {
        return buildExpressionWithRole(HAS_ROLE_PREFIX, roleAuthority)
    }

    /**
     * Returns the expression like hasAnyRole('ROLE_USER','ROLE_ADMIN').
     * @param roleAuthorities: It is the list string of role authority. AKA role name.
     * @return
     */
    static String hasAnyRole(List<String> roleAuthorities) {
        return buildExpressionWithRoles(HAS_ANY_ROLE_PREFIX, roleAuthorities)
    }

    /**
     * Returns the expression like hasNoRole('ROLE_ADMIN').
     * @param roleAuthority: It is the string of role authority. AKA role name.
     */
    static String hasNoRole(String roleAuthority) {
        return buildExpressionWithRole(HAS_NO_ROLE_PREFIX, roleAuthority)
    }

    /**
     * Returns the expression like hasNoAnyRole('ROLE_USER','ROLE_ADMIN').
     * @param roleAuthorities: It is the list string of role authority. AKA role name.
     * @return
     */
    static String hasNoAnyRole(List<String> roleAuthorities) {
        return buildExpressionWithRoles(HAS_NO_ANY_ROLE_PREFIX, roleAuthorities)
    }

    /**
     * Returns the expression like hasRole('ROLE_ADMIN').
     * @param prefix: String of expression prefix. Like hasRole, hasNoRole.
     * @param roleAuthority: It is the string of role authority. AKA role name.
     */
    private static String buildExpressionWithRole(String prefix, String roleAuthority) {
        return "$prefix('$roleAuthority')"
    }

    /**
     * Returns the expression like hasAnyRole('ROLE_USER','ROLE_ADMIN').
     * @param prefix: String of expression prefix. Like hasRole, hasNoRole.
     * @param roleAuthorities: It is the list string of role authority. AKA role name.
     * @return
     */
    private static String buildExpressionWithRoles(String prefix, List<String> roleAuthorities) {
        // The initial string of the expression.
        String result = "$prefix("

        // Goes through the list of roleAuthorities and appends it to result.
        roleAuthorities.each {
            result += "'$it',"
        }

        // Removes the last comma and closes with closing bracket.
        result = result.substring(0, result.length() - 1) + ")"

        return result
    }
}